#include "stdafx.h"
#include "CppUnitTest.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

#include <vex/core/as_intrusive.h>
#include <vex/iterable/contract/range.h>
#include <vex/functional/make_raw_multicast_delegate.h>
#include <vex/functional/make_any_delegate.h>
#include <vex.tests/mstest/tests.core/tests.core.handle_policy.h>

namespace Microsoft { namespace VisualStudio { namespace CppUnitTestFramework {
    static std::wstring ToString(std::pair<int, double> const & p_pair)
    {
        std::wstringstream t_str;
        t_str << "pair:" << p_pair.first << ":" << p_pair.second;
        return t_str.str();
    }
}}}
namespace tests { namespace tests_functional_mc_multicast_delegate_default_impl {		

    namespace vi = vex::iterable;
    namespace vf = vex::functional;
    namespace mpl = boost::mpl;
    namespace bft = boost::function_types;

    TEST_CLASS(test_class)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.functional.mc.multicast_delegate_default_impl")
        END_TEST_CLASS_ATTRIBUTE();
    public:
        //TEST_METHOD(mcd_default_impl_return_void_arity_0_reducer_catching_exceptions)
        //{
        //    std::vector<std::exception_ptr> t_thrown_ex;
        //    typedef vi::contract::range<> invoker_type;
        //    auto t_reducer_catching_exceptions = [&](invoker_type * const p_invoker) {
        //        // invokes all delegates if any, catches all exceptions:
        //        bool t_not_empty = true;
        //        while (t_not_empty) {
        //            try {
        //                t_not_empty = p_invoker->move_front();
        //            } catch (...) {
        //                t_thrown_ex.push_back(std::current_exception());
        //            }
        //        }
        //        return;
        //    };

        //    if (auto md = vex::as_intrusive(
        //        vf::make_raw_multicast_delegate<void ()>(t_reducer_catching_exceptions))) 
        //    {
        //        md->invoke();
        //        Assert::IsTrue(t_thrown_ex.empty());
        //    } else {
        //        Assert::IsTrue(false, L"make_raw_multicast_delegate failed!");
        //    }
        //}

        //TEST_METHOD(mcd_default_impl_return_int_arity_reducer_collecting_values)
        //{
        //    std::vector<int> t_results;
        //    typedef vi::contract::range<vi::bidirectional, int> invoker_type;
        //    auto t_reducer_collecting_values = [&](invoker_type* const p_invoker) -> int {
        //        while (p_invoker->move_front()) {
        //            t_results.push_back(p_invoker->front());
        //        }
        //        return t_results.size();
        //    };
        //    if (auto md = vex::as_intrusive(vf::make_raw_multicast_delegate<int ()>(t_reducer_collecting_values)))
        //    {
        //        md->invoke();
        //    } else {
        //        Assert::IsTrue(false, L"make_raw_multicast_delegate failed!");
        //    }
        //}

        TEST_METHOD(mcd_default_impl_returning_void_should_be_safely_invokable_if_no_delegate_is_connected)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            typedef void (signature_t)();
            //try {
            if (auto t_mcd = vex::as_intrusive_from_out(vf::make_raw_multicast_delegate<signature_t>())) {
                t_mcd->invoke();
            } else {
                Assert::Fail(wlog_message() << "make_raw_multicast_delegate failed!" | as_string);
            }
        }

        TEST_METHOD(mcd_default_impl_returning_reference_should_be_safely_invokable_if_no_delegate_is_connected)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            typedef int const& (signature_t)();
            const int c_default = std::numeric_limits<int>::max();
            if (auto t_mcd = vex::as_intrusive_from_out(
                vf::make_raw_multicast_delegate<signature_t>(vf::default_reducer(c_default)))) {
                Assert::AreEqual(c_default, t_mcd->invoke());
                Assert::AreEqual(&c_default, &t_mcd->invoke());
            } else {
                Assert::Fail(wlog_message() << "make_raw_multicast_delegate failed!" | as_string);
            }
        }

        TEST_METHOD(mcd_default_impl_arity_0_return_void_should_be_able_to_connect_invoke_and_disconnect_automatically)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            typedef void (signature_t)();

            int t_capture = 0;
            if (auto t_delegate = vf::make_any_delegate<signature_t>([&]() { t_capture = 42; })) {
                if (auto t_mcd = vex::as_intrusive_from_out(vf::make_raw_multicast_delegate<signature_t>())) {
                    auto t_mcd_new = vex::as_intrusive_from_out(t_mcd->combine(t_delegate.get()));
                    t_mcd_new->invoke();
                    Assert::AreEqual(42, t_capture);
                } else {
                    Assert::Fail(wlog_message() << "make_raw_multicast_delegate failed!" | as_string);
                }
            } else {
                Assert::Fail(wlog_message() << "make_any_delegate failed!" | as_string);
            }
        }

        TEST_METHOD(mcd_default_impl_arity_0_return_int_should_be_able_to_connect_invoke_and_disconnect_automatically)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            typedef int (signature_t)();
            if (auto t_delegate = vf::make_any_delegate<signature_t>([]() { return 42; })) {
                if (auto t_mcd = vex::as_intrusive_from_out(vf::make_raw_multicast_delegate<signature_t>(vf::default_reducer(std::numeric_limits<int>::max())))) {
                    auto t_mcd_new = vex::as_intrusive_from_out(t_mcd->combine(t_delegate.get()));
                    Assert::AreEqual(42, t_mcd_new->invoke());
                } else {
                    Assert::Fail(wlog_message() << "make_raw_multicast_delegate failed!" | as_string);
                }
            } else {
                Assert::Fail(wlog_message() << "make_any_delegate failed!" | as_string);
            }
        }

        TEST_METHOD(mcd_default_impl_arity_1_return_void_should_be_able_to_connect_invoke_and_disconnect_automatically)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            typedef void (signature_t)(int);

            int t_capture = 0;
            if (auto t_delegate = vf::make_any_delegate<signature_t>([&](int p_v) { t_capture = 42 + p_v; })) {
                if (auto t_mcd = vex::as_intrusive_from_out(vf::make_raw_multicast_delegate<signature_t>())) {
                    auto t_mcd_new = vex::as_intrusive_from_out(t_mcd->combine(t_delegate.get()));
                    t_mcd_new->invoke(1);
                    Assert::AreEqual(43, t_capture);
                } else {
                    Assert::Fail(wlog_message() << "make_raw_multicast_delegate failed!" | as_string);
                }
            } else {
                Assert::Fail(wlog_message() << "make_any_delegate failed!" | as_string);
            }
        }

        TEST_METHOD(mcd_default_impl_arity_5_return_void_should_be_able_to_connect_invoke_and_disconnect_automatically)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            typedef void (signature_t)(int, double, std::string const&, std::pair<int, double> const&, char);

            if (auto t_delegate = vf::make_any_delegate<signature_t>(
                [](int p1, double p2, std::string const& p3, std::pair<int, double> const& p4, char p5) {
                    Assert::AreEqual(42, p1);
                    Assert::AreEqual(42.1, p2);
                    Assert::AreEqual(std::string("bimbo"), p3);
                    Assert::AreEqual(std::make_pair<int, double>(42, 42.1), p4);
                    Assert::AreEqual('b', p5);
            })) 
            {
                if (auto t_mcd = vex::as_intrusive_from_out(vf::make_raw_multicast_delegate<signature_t>())) {
                    //auto t_mcd_new = vex::as_intrusive_from_out(t_mcd->combine(t_delegate.get()));
                    //t_mcd_new->invoke(42, 42.1, "bimbo", std::make_pair<int, double>(42, 42.1), 'b');
                } else {
                    Assert::Fail(wlog_message() << "make_raw_multicast_delegate failed!" | as_string);
                }
            }
        }
    };
}}